﻿using System;
using System.Threading;

namespace Spider
{
    public class Done   //控制下载的类（开始和结束）
    {
        private int m_activeThreads = 0; 
        private bool m_started = false;

        /**
         * 利用多个线程同时下载文件有效地提高了性能，但也带来了线程管理方面的问题。
         * 其中最复杂的一个问题是：蜘蛛程序何时才算完成了工作？在这里我们要借助一个专用的类Done来判断。
         * 首先有必要说明一下"完成工作"的具体含义。只有当系统中不存在等待下载的URL，而且所有工作线程都已经结束其处理工作时，
         * 蜘蛛程序的工作才算完成。也就是说，完成工作意味着已经没有等待下载和正在下载的URL。
         * Done类提供了一个WaitDone方法，它的功能是一直等待，直到Done对象检测到蜘蛛程序已完成工作。下面是WaitDone方法的代码。
         **/
        public void WaitDone()
        //WaitDone方法将一直等待，直到不再有活动的线程。但必须注意的是，下载
        //开始的最初阶段也没有任何活动的线程，所以很容易造成蜘蛛程序一开始就立即停止的现象。
        //为解决这个问题，我们还需要另一个方法WaitBegin来等待蜘蛛程序进入”正式的“工作阶段。
        //一般的调用次序是：先调用WaitBegin，再接着调用WaitDone，WaitDone将等待蜘蛛程序完成工作。
        {
            Monitor.Enter(this);  //在指定对象上获取排他锁，是为了获取m_activeThreads的值
            while (m_activeThreads > 0) //如果还有活动的线程
            {
                Monitor.Wait(this);
            }
            Monitor.Exit(this);
        }
        public void WaitBegin()
        {
            Monitor.Enter(this);
            while (!m_started)
            {
                Monitor.Wait(this);
            }
            Monitor.Exit(this);
        }
        public void WorkerBegin()
        {
            Monitor.Enter(this);
            m_activeThreads++;
            m_started = true;
            Monitor.Pulse(this);
            Monitor.Exit(this);
        }
        public void WorkerEnd() //Done类利用WorkerBegin和WorkerEnd方法来确定何时整个下载操作已经完成
        {
            Monitor.Enter(this);
            m_activeThreads--;
            Monitor.Pulse(this); //通知等待队列中的线程锁定对象状态的更改
            Monitor.Exit(this);
        }
        public void Reset()
        {
            Monitor.Enter(this);
            m_activeThreads = 0;
            Monitor.Exit(this);
        }
    }
}
